package frontend;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;

import model.BookMasterTableModel;
import model.CopyListModel;
import model.NewLoansOfPersonTableModel;
import net.miginfocom.swing.MigLayout;

import components.BookDetailCellRenderer;

import domain.Book;
import domain.Copy;
import domain.Customer;
import domain.Library;
import factory.DisposeOnEscapeFactory;

public class CreateLoanJFrame extends JFrame {

    /**
	 * 
	 */
    private static final long serialVersionUID = 392789745267259012L;
    private static final int MAX_AUSLEIHEN = 3;
    private JPanel contentPane;
    private JTextField txtSearchBooks;
    private JTable tblBooks;
    private JList<Copy> listCopies;
    private JTable tblLoans;
    private JCheckBox chkNurVerfgbare;
    private TableRowSorter<BookMasterTableModel> tblBooksSorter;
    private Customer customer;
    private Library library;
    private List<RowFilter<Object, Object>> bookTableFilters = new ArrayList<RowFilter<Object, Object>>(2);
    private String bookTableTextFilter = "";

    private static HashMap<Customer, CreateLoanJFrame> instance = new HashMap<>();
    private JLabel lblBereitsAusgeliehenValue;
    private JLabel lblMaxNeueAusleihenValue;
    private JLabel lblUberflligeAusleihenValue;
    private JButton btnAusleihen;
    private JButton btnEntfernen;
    private JButton btnSave;
    private JButton btnAbbrechen;
    private Component horizontalStrut;

    public static CreateLoanJFrame getInstance(Customer customer) {
        if (!instance.containsKey(customer))
            instance.put(customer, new CreateLoanJFrame(customer));
        return instance.get(customer);
    }

    /**
     * Create the frame.
     */
    public CreateLoanJFrame(Customer customer) {
        this.customer = customer;
        this.library = Library.getInstance();
        DisposeOnEscapeFactory.appendDisposeOnEscapeToJFrame(this);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        setBounds(100, 100, 750, 550);
        setMinimumSize(new Dimension(750, 550));
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new MigLayout("", "[grow]", "[grow][150px:150px][grow][][]"));

        JPanel panel = new JPanel();
        panel.setBorder(new TitledBorder(null, "B\u00FCcher", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        contentPane.add(panel, "cell 0 0,grow");
        panel.setLayout(new MigLayout("", "[grow]", "[][grow]"));

        txtSearchBooks = new JTextField();
        txtSearchBooks.setText("Suche");
        txtSearchBooks.setColumns(10);
        panel.add(txtSearchBooks, "flowx,cell 0 0,growx");

        chkNurVerfgbare = new JCheckBox("Nur Verfügbare");
        chkNurVerfgbare.setSelected(true);
        panel.add(chkNurVerfgbare, "cell 0 0");

        tblBooks = new JTable();
        tblBooks.setModel(BookMasterTableModel.getInstance());
        tblBooks.getColumnModel().getColumn(0).setMinWidth(110);
        tblBooks.getColumnModel().getColumn(0).setMaxWidth(110);
        tblBooks.getColumnModel().getColumn(1).setMinWidth(250);
        tblBooksSorter = new TableRowSorter<>(BookMasterTableModel.getInstance());
        tblBooks.setRowSorter(tblBooksSorter);
        tblBooks.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        panel.add(new JScrollPane(tblBooks), "cell 0 1,grow");

        JPanel panel_1 = new JPanel();
        panel_1.setBorder(new TitledBorder(null, "Exemplare", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        contentPane.add(panel_1, "cell 0 1,grow");
        panel_1.setLayout(new MigLayout("", "[grow]", "[grow][]"));

        listCopies = new JList<>();
        listCopies.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        listCopies.setCellRenderer(new BookDetailCellRenderer());
        listCopies.setBorder(BorderFactory.createLineBorder(Color.black));
        panel_1.add(new JScrollPane(listCopies), "cell 0 0,grow");

        btnAusleihen = new JButton("Ausleihen");
        btnAusleihen.setEnabled(false);
        panel_1.add(btnAusleihen, "cell 0 1,alignx right");

        JPanel panel_2 = new JPanel();
        panel_2.setBorder(new TitledBorder(null, "Neue Ausleihen", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        contentPane.add(panel_2, "cell 0 2,grow");
        panel_2.setLayout(new MigLayout("", "[][][][][][][grow]", "[][grow][][]"));

        JLabel lblBereitsAusgeliehen = new JLabel("Bereits ausgeliehen:");
        panel_2.add(lblBereitsAusgeliehen, "cell 0 0");

        lblBereitsAusgeliehenValue = new JLabel("0");
        panel_2.add(lblBereitsAusgeliehenValue, "cell 1 0");

        JLabel lblMaxNeueAusleihen = new JLabel("Max. neue Ausleihen");
        panel_2.add(lblMaxNeueAusleihen, "cell 2 0");

        lblMaxNeueAusleihenValue = new JLabel("0");
        panel_2.add(lblMaxNeueAusleihenValue, "cell 3 0");

        JLabel lblUberflligeAusleihen = new JLabel("Überfällige Ausleihen:");
        panel_2.add(lblUberflligeAusleihen, "cell 4 0");

        lblUberflligeAusleihenValue = new JLabel("0");
        panel_2.add(lblUberflligeAusleihenValue, "cell 5 0");

        tblLoans = new JTable();
        tblLoans.setModel(NewLoansOfPersonTableModel.getInstance());
        tblLoans.getColumnModel().getColumn(0).setMinWidth(40);
        tblLoans.getColumnModel().getColumn(0).setMaxWidth(40);
        tblLoans.getColumnModel().getColumn(1).setMinWidth(250);
        tblLoans.getColumnModel().getColumn(2).setMinWidth(80);
        tblLoans.getColumnModel().getColumn(3).setMinWidth(80);
        panel_2.add(new JScrollPane(tblLoans), "cell 0 1 7 1,grow");

        btnEntfernen = new JButton("Entfernen");
        btnEntfernen.setEnabled(false);
        panel_2.add(btnEntfernen, "cell 6 0,alignx right");

        horizontalStrut = Box.createHorizontalStrut(20);
        contentPane.add(horizontalStrut, "flowx,cell 0 3,alignx right");

        btnSave = new JButton("Speichern");
        contentPane.add(btnSave, "cell 0 3");
        btnSave.setEnabled(false);

        btnAbbrechen = new JButton("Abbrechen");
        contentPane.add(btnAbbrechen, "cell 0 3");

        attachActionListeners();
        updateButtonsAndLabels();
        updateLoansFilter();
        setShortcuts();
    }

    private void setShortcuts() {
        btnAusleihen.setMnemonic(KeyEvent.VK_A);
        btnEntfernen.setMnemonic(KeyEvent.VK_E);
        btnSave.setMnemonic(KeyEvent.VK_S);
        btnAbbrechen.setMnemonic(KeyEvent.VK_B);
        chkNurVerfgbare.setMnemonic(KeyEvent.VK_V);
    }

    @Override
    public void dispose() {
        instance.remove(customer);
        super.dispose();
    }

    private void updateLoansFilter() {
        bookTableFilters.clear();
        bookTableFilters.add(RowFilter.regexFilter(bookTableTextFilter, 1, 2, 3));
        if (chkNurVerfgbare.isSelected()) {
            bookTableFilters.add(RowFilter.regexFilter("^((?!ab).)*$", 0));
        }
        tblBooksSorter.setRowFilter(RowFilter.andFilter(bookTableFilters));
    }

    private void attachActionListeners() {
        txtSearchBooks.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                bookTableTextFilter = txtSearchBooks.getText();
                updateLoansFilter();
            }
        });

        listCopies.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent arg0) {
                updateButtonsAndLabels();
            }
        });
        chkNurVerfgbare.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateLoansFilter();
            }
        });
        txtSearchBooks.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                if (txtSearchBooks.getText().equals("Suche")) {
                    txtSearchBooks.setText("");
                }
            }

            @Override
            public void focusLost(FocusEvent e) {
                if (txtSearchBooks.getText().isEmpty()) {
                    txtSearchBooks.setText("Suche");
                }
            }
        });
        tblBooks.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                int rowIndex = tblBooks.getSelectedRow();
                if (rowIndex >= 0) {
                    Book book = BookMasterTableModel.getInstance().getElementAt(tblBooks.convertRowIndexToModel(tblBooks.getSelectedRow()));
                    listCopies.setModel(CopyListModel.getInstance(book));
                } else {
                    listCopies.setModel(CopyListModel.getInstance(null));
                }
            }
        });

        btnAusleihen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                addLoan();
            }

        });

        btnEntfernen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                removeLoan();
            }
        });

        tblLoans.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent arg0) {
                updateButtonsAndLabels();
            }
        });
        btnSave.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createLoans();
            }
        });

        btnAbbrechen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                CreateLoanJFrame.this.dispose();
            }
        });

        Action showBookAction = new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent arg0) {
                showSelectedBook();
            }
        };

        tblBooks.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "showBook");
        tblBooks.getActionMap().put("showBook", showBookAction);

        tblBooks.addMouseListener(new MouseListener() {
            @Override
            public void mouseReleased(MouseEvent e) {}

            @Override
            public void mousePressed(MouseEvent e) {}

            @Override
            public void mouseExited(MouseEvent e) {}

            @Override
            public void mouseEntered(MouseEvent e) {}

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() >= 2) {
                    showSelectedBook();
                }
            }
        });

        listCopies.addMouseListener(new MouseListener() {
            @Override
            public void mouseReleased(MouseEvent e) {}

            @Override
            public void mousePressed(MouseEvent e) {}

            @Override
            public void mouseExited(MouseEvent e) {}

            @Override
            public void mouseEntered(MouseEvent e) {}

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() >= 2)
                    addLoan();
            }
        });

        tblLoans.addMouseListener(new MouseListener() {
            @Override
            public void mouseReleased(MouseEvent e) {}

            @Override
            public void mousePressed(MouseEvent e) {}

            @Override
            public void mouseExited(MouseEvent e) {}

            @Override
            public void mouseEntered(MouseEvent e) {}

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() >= 2)
                    removeLoan();
            }
        });
    }

    private void addLoan() {
        if (!btnAusleihen.isEnabled())
            return;

        Copy copy = listCopies.getSelectedValue();
        if (copy.isLent())
            return;
        getCurrentLoansModel().addCopy(copy);
        updateButtonsAndLabels();
    }

    private void removeLoan() {
        if (!btnEntfernen.isEnabled())
            return;

        NewLoansOfPersonTableModel loansModel = getCurrentLoansModel();
        Copy copy = loansModel.getElementAt(tblLoans.getSelectedRow());
        loansModel.removeCopy(copy);
        updateButtonsAndLabels();
    }

    private NewLoansOfPersonTableModel getCurrentLoansModel() {
        return ((NewLoansOfPersonTableModel) tblLoans.getModel());
    }

    private void showSelectedBook() {
        int rowIndex = tblBooks.getSelectedRow();
        if (rowIndex >= 0) {
            Book book = BookMasterTableModel.getInstance().getElementAt(tblBooks.convertRowIndexToModel(rowIndex));
            BookDetailJFrame.getInstance(book).toFront();
            BookDetailJFrame.getInstance(book).setVisible(true);
        }
    }

    private void createLoans() {
        NewLoansOfPersonTableModel model = getCurrentLoansModel();
        List<Copy> copies = model.getCopies();
        boolean isLent = false;
        for (Copy copy : copies) {
            if (copy.isLent()) {
                isLent = true;
                model.removeCopy(copy);

            }
        }
        if (isLent) {
            JOptionPane.showConfirmDialog(this, "Eines der ausgewählten Bücher wurde bereits ausgeliehen. Bitte überprüfen Sie ihre Auswahl.",
                    "Bereits ausgeliehen?", JOptionPane.OK_OPTION, JOptionPane.WARNING_MESSAGE);
            return;
        }
        for (Copy copy : copies) {
            library.createAndAddLoan(customer, copy);
        }
        this.dispose();
    }

    private void updateButtonsAndLabels() {
        int numCurrentLoans = library.getCustomerOpenLoans(customer).size();
        int numOverdueLoans = library.getCustomerOverdueLoans(customer).size();
        int maxNewLoans = (numOverdueLoans == 0 ? MAX_AUSLEIHEN - numCurrentLoans : 0);

        {
            int selectedRow = tblLoans.getSelectedRow();
            Copy copy = getCurrentLoansModel().getElementAt(selectedRow);
            btnEntfernen.setEnabled(copy != null);
        }

        {
            Copy copy = listCopies.getSelectedValue();
            NewLoansOfPersonTableModel model = getCurrentLoansModel();
            btnAusleihen.setEnabled(copy != null && !copy.isLent() && !model.containsCopy(copy));

            btnSave.setEnabled(tblLoans.getRowCount() != 0 && tblLoans.getRowCount() <= maxNewLoans);
        }

        {
            lblBereitsAusgeliehenValue.setText("" + numCurrentLoans);
            lblMaxNeueAusleihenValue.setText("" + maxNewLoans);
            lblUberflligeAusleihenValue.setText("" + numOverdueLoans);
        }
    }
}
